#include "VideoReader.h"

/******************************************************************************/
// for opencv

class VideoReader_OpenCV: public VideoReader
{
public:
	virtual ~VideoReader_OpenCV() {
		if (isOpened()) Close();
	}
	
	bool isOpened() const {
		return vcap.isOpened();
	}
	bool Open(const std::string& filename) {
		return vcap.open(filename);
	}
	void Close() {
		vcap.release();
	}
	
	bool Grab() {
		return vcap.grab();
	}
	bool Retrieve(cv::Mat& frame) {
		return vcap.retrieve(frame);
	}
	bool Read(cv::Mat& frame) {
		return vcap.read(frame);
	}
	
	int GetWidth() const {
		return vcap.get(cv::CAP_PROP_FRAME_WIDTH);
	}
	int GetHeight() const {
		return vcap.get(cv::CAP_PROP_FRAME_HEIGHT);
	}
	double GetFPS() const {
		return vcap.get(cv::CAP_PROP_FPS);
	}

private:
	cv::VideoCapture vcap;
};

/******************************************************************************/
// for ffmpeg

#ifdef HAVE_FFMPEG_LIB

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavutil/pixdesc.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>

class VideoReader_FFMpeg: public VideoReader
{
public:
	VideoReader_FFMpeg() {
		memset(&mBGRPicture, 0, sizeof(mBGRPicture));
		memset(&mPacket, 0, sizeof(mPacket));
		av_init_packet(&mPacket);
	}
	virtual ~VideoReader_FFMpeg() {
		if (isOpened()) Close();
	}
		
	bool isOpened() const {
		return mOpened;
	}
	
	bool Open(const std::string& filename);
	void Close();
	bool Grab();
	bool Retrieve(cv::Mat& frame);
	
	bool Read(cv::Mat& frame) {
		return ( Grab() && Retrieve(frame) );
	}
	
	int GetWidth() const {
		return mWidth;
	}
	int GetHeight() const {
		return mHeight;
	}
	double GetFPS() const {
		return mFps;
	}

private:
	static int DefaultAVInterruptCallBack(void *ptr);
	
	int mVideoIndex = -1;
	AVStream *mVideoStream = nullptr;
	bool mOpened = false;
	int64_t mLastReadPacketTime = 0;
	AVFormatContext *mCtx = nullptr;
	AVCodec *mDecode = nullptr;
	AVCodecContext *mDecodeCtx = nullptr;
	struct SwsContext *mImgConvertCtx = nullptr;
	
	int mWidth = 0;
	int mHeight = 0;
	double mFps = 0;
	
	AVFrame *mPicture = nullptr;
	AVFrame mBGRPicture{};
	AVPacket mPacket{};
}

bool VideoReader_FFMpeg::Open(const std::string& filename)
{ // TODO
}

void VideoReader_FFMpeg::Close()
{ // TODO
}

bool VideoReader_FFMpeg::Grab()
{ // TODO
}

bool VideoReader_FFMpeg::Retrieve(cv::Mat& frame)
{ // TODO
}

#endif

/******************************************************************************/

std::shared_ptr<VideoReader> VideoReader::Create(const std::string& lib)
{
	if (lib == "opencv") {
		return std::make_shared<VideoReader_OpenCV>();
#ifdef HAVE_FFMPEG_LIB
	} else if (lib == "ffmpeg") {
		return std::make_shared<VideoReader_FFMpeg>();
#endif
	}
	return nullptr;
}

bool FrameCompare::absdiff(const cv::Mat& frame)
{
	cv::Mat imag;
	cv::cvtColor(frame, imag, cv::COLOR_RGB2GRAY);
	if (!_frame.empty() && compare(imag, _frame) < _scale) {
		return false;
	}
	_frame = imag;
	return true;
}

double FrameCompare::compare(const cv::Mat& src, const cv::Mat& dst)
{
	cv::Mat diff;
	cv::absdiff(src, dst, diff);
	cv::threshold(diff, diff, _threshold, 255, cv::THRESH_BINARY);
	
	double n = 0;
	for (int i = 0; i < diff.rows; i++) {
		uchar *data = diff.ptr<uchar>(i);
		for (int j = 0; j < diff.cols; j++) {
			if (data[j] == 255) n++;
		}
	}
	return n / (double)(diff.rows*diff.cols);
}
